package gaml.java;

import gaml.error.GamlException;
import gaml.yaml.EmptyNode;
import gaml.yaml.YamlNode;
import gaml.yaml.ahchor.YamlAnchorCodeStore;
import gaml.yaml.ahchor.YamlAnchorObjectStore;


/**
 * node trees from java objects;
 * java objects from node trees
 * @author shnireck
 *
 */
public class NodeObjectBridge <T> {
	ClassSerializerStore store = null;
	
	ClassSerializer <T> serializer = null; 
	
	YamlNode node = null;
	T object = null;
	Class <T> clazz = null;

	/**
	 * create new node-object bridge for specified node, object and object's class
	 * @param node yaml node
	 * @param object java object
	 * @param clazz object's java class
	 * @param store base class serializers store to use
	 * for object serialization and deserialization
	 * @param bss base class serialization store, to get base class serializers from
	 */
	public static <T> NodeObjectBridge <T> create (YamlNode node, T object, Class <T> clazz, ClassSerializerStore store) {
		return new NodeObjectBridge <T> (node, object, clazz, store);
	}
	
	/**
	 * create new node-object bridge for specified node and new object instance
	 * @param object java object
	 * @param clazz object's java class
	 * @param store base class serializers store to use
	 * for object serialization and deserialization
	 * @param bss base class serialization store, to get base class serializers from
	 */
	public static <T> NodeObjectBridge <T> create (T object, YamlNode parent, Class <T> clazz, ClassSerializerStore store) {
		return new NodeObjectBridge <T> (object, parent, clazz, store);
	}
	
	/**
	 * create new node-object bridge for specified node, object and object's class
	 * @param node yaml node
	 * @param clazz object's java class
	 * @param store base class serializers store to use
	 * for object serialization and deserialization
	 * @param bss base class serialization store, to get base class serializers from
	 * @throws GamlException can't instantate clazz instance
	 */
	public static <T> NodeObjectBridge <T> create (YamlNode node, Class <T> clazz, ClassSerializerStore store) throws GamlException {
		return new NodeObjectBridge <T> (node, clazz, store);
	}
	
	/**
	 * get node, related to this bridge
	 * @return yaml node
	 */
	public YamlNode getNode () { return node; }
	
	/**
	 * specify new node to process for already existen bridge
	 * @param node new node
	 * @return host bridge object
	 */
	public NodeObjectBridge <T> setNode (YamlNode node) { this.node = node; return this; }

	/**
	 * get object, related to this bridge
	 * @return java object
	 */
	public T getObject () { return object; }
	
	/**
	 * get serializer, that will be used to process node and object
	 * @return serializer for clazz Class <T>
	 */
	public ClassSerializer <T> getSerializer () { return serializer; }
	
	/**
	 * get data from bridge's object, for specified bridge's clazz's
	 * fields and put it into bridge's node
	 * @param anchors yaml node anchors collection
	 * @return host bridge object
	 * @throws GamlException 
	 */
	public NodeObjectBridge <T> parseObject (YamlAnchorObjectStore anchors) throws GamlException {
		node = AnchorSerializer.serialize (anchors, serializer, node, object);
		return this;
	}

	/**
	 * get data from bridge's node for specified bridge's clazz's
	 * fields and put it into bridge's object
	 * @param anchors yaml node anchors collection
	 * @return host bridge object
	 * @throws GamlException 
	 */
	public NodeObjectBridge <T> parseNode (YamlAnchorCodeStore anchors) throws GamlException {
		object = AnchorSerializer.restore (anchors, serializer, node, object, store);
		return this;
	}
	
	protected NodeObjectBridge (YamlNode node, T object, Class <T> clazz, ClassSerializerStore store) {
		assign (store, clazz, node);
		this.object = object;
		
		serializer = getSerializer (store, (((node instanceof EmptyNode) || (null == object)) ? null : clazz));
	}

	protected NodeObjectBridge (T object, YamlNode parent, Class <T> clazz, ClassSerializerStore store) {
		assign (store, clazz, null);
		this.object = object;
		
		serializer = getSerializer (store, (null == object ? null : clazz));
		this.node = serializer.createNode (parent);
	}
	
	protected NodeObjectBridge (YamlNode node, Class <T> clazz, ClassSerializerStore store) throws GamlException {
		assign (store, clazz, node);
		serializer = getSerializer (store, (node instanceof EmptyNode ? null : clazz));
		
		this.object = serializer.createObject ();
	}
	
	/**
	 * set values for class, serializers store and yaml node
	 * @param store serializers store to use
	 * @param clazz class to serialize-deserialize it's instance
	 * @param node yaml node to make serializer for
	 */
	protected void assign (ClassSerializerStore store, Class <T> clazz, YamlNode node) {
		this.clazz = clazz;
		this.store = store;
		this.node = node;
	}
	
	/** get serializer for class from store
	 * @param <E> serializer type
	 * @param store serializers store
	 * @param clazz java class, to get serializer for
	 * @return serializer for java class
	 */
	protected <E> ClassSerializer <E> getSerializer (ClassSerializerStore store, Class <E> clazz) { return (store.contains (clazz) ? store.get (clazz) : ((clazz.isEnum () ? store.getForEnum (clazz) : store.getDefault (clazz)))); }
}